package com.cityu.bac.rfidDriver;

import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

class PacketProcess {
	
	private static final String TAG = "RfidPacketProcess";

	private InputStream in;
	private OutputStream out;
	
	private boolean reading = false;
	private boolean stopReadBytes = false;
	
	void stopReadBytes(boolean stop){
    	stopReadBytes = stop;
    }	
	
	PacketProcess(CommandProcess cmdProcess,SerialPort sp)
	{
		in = sp.getInputStream();
		out = sp.getOutputStream();
		try {
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
	}
	
	/**********************************************************************
     * 
     * Check Reading
     * 
     **********************************************************************/
	 boolean isReading()
    {
        return reading;
    }
	
	/**********************************************************************
     * 
     * Setting Reading
     * 
     **********************************************************************/
	void setReading(boolean reading)
    {
        this.reading = reading;
    }
	
	/**********************************************************************
     * 
     * Send - Used by Start & Stop Request
	 * @throws IOException 
	 * @throws Exception 
     * 
     **********************************************************************/
    
    private void send(byte[] data) throws IOException
    {
		out.write(data, 0, data.length);
	    out.flush();
    }
    /**********************************************************************
     * 
     * Send - Used by others
     * @throws Exception 
     * 
     **********************************************************************/
    void sendCommand(byte[] data) throws Exception
    {
    	stopReadBytes = false;        
        
        for(int i = 0;i < 5;i++){
        	try{
        		this.send(data);
        	}catch(Exception e){
        		continue;
        	}
        	break;
        }
                
    }
    
    byte[] getResponse() throws Exception{
        return getResponse(1000);
    }
    
    /**********************************************************************
     * 
     * Response Framer
     * @throws Exception 
     * 
     **********************************************************************/
    byte[] getResponse(int timeout) throws Exception
    {
        byte[] frame = null;
        frame = this.getFrame(timeout);        
        return frame;
    }
    
    private byte[] getBytes(int expects) throws Exception
    {
    	return getBytes(expects,1000);
    }
    
    private byte[] getBytes(int expects, int timeout) throws Exception
    {
    	if(expects > 1024)
    		return null;
    	
        byte[] bytes = new byte[expects];
        int readedlength = 0;
        
        long time = System.currentTimeMillis();
        
        while (true)
        {
        	if(stopReadBytes)
        		return null;
        	int len = expects - readedlength;
        	try{
        	readedlength += in.read(bytes, readedlength, len);
        	}catch(IOException ioe){
        		if(ioe.getMessage().equals("Software caused connection abort")){
        			throw ioe;
        		}
        		if(System.currentTimeMillis() - time > timeout)
        		    throw new Exception("get data from serial port timeout!");
        		try {
					Thread.sleep(80);
				} catch (InterruptedException e) {
//					e.printStackTrace();
				}
        	}
        	if(readedlength >= expects)
        		break;
        } 

        return bytes;
    }

    byte[] getFrame(int timeout) throws Exception
    {
        Log.d(TAG,"Entry to getFrame...");

        byte[] head = null;
        short msgLen = 0;

        head = this.getBytes(3, timeout);
        msgLen = (short) (((short)head[1] << 8) + (short)head[2]);

        byte[] resp = this.getBytes(msgLen + 1, timeout);

        byte end = resp[msgLen];
        if(end != Command.endbyte)
        {
            throw new Exception("Invalid Frame End");
        }

        byte[] frame = new byte[resp.length + 5];

        frame[0] = Command.startbyte;
        frame[1] = Command.typeResp;
        frame[2] = head[0];
        frame[3] = head[1];
        frame[4] = head[2];

        System.arraycopy(resp, 0, frame, 5, resp.length);
        
        Log.d(TAG,"frame: "+ Utils.ByteToHex(frame));
        return frame;
    }
    
    int getRxDateType(){
        byte[] b = null;
        try {
            b = getBytes(2,100);
            if(b != null)
                Log.d(TAG, "getRxDataType: "+Utils.ByteToHex(b));
        } catch (Exception e) {
        	if(e.getMessage().equals("Software caused connection abort"))
        		return -2;
        	else
        		return -1;
        }
        if (b != null && b[0] == Command.startbyte)
            return b[1];
        return -1;
    }
    
    /**********************************************************************
     * 
     *  Notification Framer
     *  
     *  notiPhase = 0: searhing for start byte
     *  notiPhase = 1: waiting for 4 bytes head
     *  notiPhase = 2: waiting for payllad & end byte
     *  Initial notiPhase = 0 when start/stop read
     *  
     **********************************************************************/

    private int payloadLen = -1;

    byte[] getNoti(int timeout) throws Exception
    {
        byte[] packet = getNotiPacket();
        if (packet != null)
        {   
            if(packet[1] == Command.typeNotify)
            {                   
                byte[] payload = new byte[payloadLen];
                System.arraycopy(packet, 5, payload, 0, payloadLen);
                Log.d(TAG,"Packat: " + Utils.ByteToHex(packet));
                return payload;
            }
        }

        return null;
    }

    private byte[] getNotiPacket() throws Exception
    {
        byte[] p;
        byte[] head = getBytes(3);

        payloadLen = (short)(((short)head[1] << 8) + (short)head[2]);
        int len = payloadLen + 1;
            
        p = new byte[len + 5];
        p[0] = Command.startbyte;
        p[1] = Command.typeNotify;
        p[2] = head[0];
        p[3] = head[1];
        p[4] = head[2];
        
        byte[] data = new byte[len];
        data = getBytes(len);
        if(data == null)
            return null;
        System.arraycopy(data, 0, p, 5, len);
        
        if (!checkNotiPacket(p))
        {
            Log.d(TAG,"Invalid Notification: " + Utils.ByteToHex(p));
            return null;
        }

        return p;
    }

    private boolean checkNotiPacket(byte[] packet)
    {
        if (packet[packet.length - 1] == Command.endbyte)
        {
            return true;
        }
        return false;
    }

}


